
import java.util.Random;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.midlet.MIDletStateChangeException;

public class GameEngine extends Canvas implements CommandListener, Runnable {

	static final int CMD_EXIT = 0;
	static final int CMD_PAUSE = 1;
	static final int CMD_RESUME = 2;
	static final int CMD_NEW = 3;
	static final int CMD_OPTIONS = 4;
	static final int CMD_HELP = 5;
	static final int CMD_ZLAST = 6;

	static final int INIT = 0;
	static final int PLAYING = 1;
	static final int PAUSED = 2;
	static final int WON = 3;

	private NPuzzleMidlet midlet;
	private Command[] cmd;
	private int gameState;

	static int nmoves;

	Font fontL;
	Font fontM;

	int cellz;
	int cellxoff, cellyoff;
	int gridz, gridx, gridy;

	static int[] RowIndex;
	static int[] ColIndex;

	public GameMatrix matrix;
	GameOptions options;

	public GameEngine(NPuzzleMidlet midlet) {
		// TODO Auto-generated constructor stub
		
		new Thread(this).start();
		
		options = new GameOptions();

		this.midlet = midlet;

		fontL = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
				Font.SIZE_LARGE);
		fontM = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
				Font.SIZE_MEDIUM);

		cmd = new Command[CMD_ZLAST];
		cmd[CMD_EXIT] = new GameCommand("Exit", Command.EXIT, 1, CMD_EXIT);
		cmd[CMD_PAUSE] = new GameCommand("Pause", Command.SCREEN, 2, CMD_PAUSE);
		cmd[CMD_RESUME] = new GameCommand("Resume game", Command.SCREEN, 2,
				CMD_RESUME);
		cmd[CMD_NEW] = new GameCommand("New game", Command.SCREEN, 3, CMD_NEW);
		cmd[CMD_OPTIONS] = new GameCommand("Options", Command.SCREEN, 3,
				CMD_OPTIONS);
		cmd[CMD_HELP] = new GameCommand("Help", Command.SCREEN, 3, CMD_HELP);

		setCommandListener(this);
		InitGame();
		setState(INIT);
	}

	private void setState(int state) {
		gameState = state;

		switch (gameState) {
		case INIT:
			addCommand(cmd[CMD_EXIT]);
			addCommand(cmd[CMD_NEW]);
			addCommand(cmd[CMD_OPTIONS]);
			addCommand(cmd[CMD_HELP]);
			break;

		case PLAYING:
			removeCommand(cmd[CMD_RESUME]);
			addCommand(cmd[CMD_EXIT]);
			addCommand(cmd[CMD_PAUSE]);
			addCommand(cmd[CMD_NEW]);
			addCommand(cmd[CMD_OPTIONS]);
			addCommand(cmd[CMD_HELP]);
			break;

		case PAUSED:
			removeCommand(cmd[CMD_PAUSE]);
			addCommand(cmd[CMD_EXIT]);
			addCommand(cmd[CMD_RESUME]);
			addCommand(cmd[CMD_NEW]);
			addCommand(cmd[CMD_OPTIONS]);
			addCommand(cmd[CMD_HELP]);
			break;

		case WON:
			removeCommand(cmd[CMD_PAUSE]);
			removeCommand(cmd[CMD_RESUME]);
			addCommand(cmd[CMD_EXIT]);
			addCommand(cmd[CMD_NEW]);
			addCommand(cmd[CMD_OPTIONS]);
			addCommand(cmd[CMD_HELP]);
			break;
		}
	}

	public void InitGame() {
		matrix = new GameMatrix(options.level);

		cellz = (getWidth() - 14) / options.level;
		cellxoff = cellz / 2;
		cellyoff = (cellz - fontL.getHeight()) / 2;

		gridz = cellz * options.level;
		gridx = (getWidth() - gridz) / 2 + 1;
		gridy = 7;

		RowIndex = new int[matrix.boardz];
		ColIndex = new int[matrix.boardz];
		for (int i = 0; i < matrix.boardz; i++) {
			ColIndex[i] = i % options.level;
			RowIndex[i] = i / options.level;

			System.out.print(matrix.board[i]);
			System.out.print(" ");
		}

		System.out.println(matrix.blankp);
	}

	public void NewGame() {
		InitGame();
		Shuffle();
		repaint();
		setState(PLAYING);
		for (int i = 0; i < matrix.boardz; i++) {
			System.out.print(matrix.board[i]);
			System.out.print(" ");
		}

		System.out.println(matrix.blankp);

		if (CanSolve()) {
			System.out.println("Can solve!");
			Solve();
		} else {
			System.out.println("Can't solve!");
		}
	}

	public boolean CanSolve() {
		int count = 0;

		for (int i = 0; i < matrix.boardz; i++) {
			int t = matrix.board[i];
			if (t > 1) {
				for (int j = i + 1; j < matrix.boardz; j++) {
					if (matrix.board[j] != 0 && matrix.board[j] < t)
						count++;
				}
			}
		}

		System.out.print("count: ");
		System.out.println(count);

		if (matrix.size % 2 == 1) {
			return (count % 2 == 0);
		} else {
			return (count % 2 == (RowIndex[matrix.blankp] + 1) % 2);
		}
	}

	public void Solve() {
		AStar aStar = new AStar(this);
		aStar.FindSolution();

		while (!aStar.Solution.isEmpty()) {
			
			int i = ((Node) aStar.Solution.pop()).direction;
			matrix.MakeMove(i);
			repaint();
			serviceRepaints();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(i);
		}
	}

	public void Shuffle() {

		Random rand = new Random();
		// int direction;
		int j;

		for (int i = 0; i < 5; i++) {
			j = rand.nextInt(matrix.boardz);
			matrix.Swap(i, j);
		}

		for (int i = 0; i < matrix.boardz; i++) {
			if (matrix.board[i] == GameMatrix.BLANK) {
				matrix.blankp = i;
				break;
			}
		}
		/*
		 * for (int i = 0; i < 100; i++) { direction = rand.nextInt(5); switch
		 * (direction) { case MoveDirection.UP: if (matrix.CanMoveUp()) {
		 * matrix.MakeMove(direction); } break; case MoveDirection.DOWN: if
		 * (matrix.CanMoveDown()) { matrix.MakeMove(direction); } break; case
		 * MoveDirection.LEFT: if (matrix.CanMoveLeft()) {
		 * matrix.MakeMove(direction); } break; case MoveDirection.RIGHT: if
		 * (matrix.CanMoveRight()) { matrix.MakeMove(direction); } break; } }
		 */
	}

	protected void paint(Graphics g) {
		// TODO Auto-generated method stub
		g.setColor(0xFFFFFFFF);
		g.fillRect(0, 0, getWidth(), getHeight());
		g.setColor(0);
		g.setFont(fontL);
		g.translate(gridx, gridy);
		g.drawRect(-2, -2, gridz + 2, gridz + 2);
		g.drawString(Integer.toString(nmoves), 0, gridz + 6, Graphics.LEFT
				| Graphics.TOP);

		for (int i = 0; i < matrix.boardz; i++) {
			if (matrix.board[i] > 0) {
				String label = Integer.toString(matrix.board[i]);
				int px = ColIndex[i] * cellz;
				int py = RowIndex[i] * cellz;

				g.setColor(0x7DFF66);
				g.fillRect(px, py, cellz - 1, cellz - 1);
				g.setColor(0);
				g.drawString(label, px + cellxoff, py + cellyoff,
						Graphics.HCENTER | Graphics.TOP);
			}
		}
	}

	protected void keyPressed(int keyCode) {
		if (gameState != PLAYING)
			return;

		int action = getGameAction(keyCode);

		switch (action) {
		case Canvas.UP:
			if (matrix.CanMoveUp()) {
				matrix.MakeMove(MoveDirection.UP);
				repaint();
			}
			break;

		case Canvas.DOWN:
			if (matrix.CanMoveDown()) {
				matrix.MakeMove(MoveDirection.DOWN);
				repaint();
			}
			break;

		case Canvas.LEFT:
			if (matrix.CanMoveLeft()) {
				matrix.MakeMove(MoveDirection.LEFT);
				repaint();
			}
			break;

		case Canvas.RIGHT:
			if (matrix.CanMoveRight()) {
				matrix.MakeMove(MoveDirection.RIGHT);
				repaint();
			}
			break;
		}
	}

	protected void pointerPressed(int x, int y) {
		if (gameState != PLAYING)
			return;

		int swap = x / cellz + y / cellz * options.level;

		if (swap < 0 || swap > matrix.boardz - 1)
			return;

		if (swap == matrix.blankp + 1 && matrix.CanMoveRight()) {
			nmoves++;
			matrix.MakeMove(MoveDirection.RIGHT);
			repaint();
			return;
		}

		if (swap == matrix.blankp - 1 && matrix.CanMoveLeft()) {
			nmoves++;
			matrix.MakeMove(MoveDirection.LEFT);
			repaint();
			return;
		}

		if (swap == matrix.blankp + options.level && matrix.CanMoveDown()) {
			nmoves++;
			matrix.MakeMove(MoveDirection.DOWN);
			repaint();
			return;
		}

		if (swap == matrix.blankp - options.level && matrix.CanMoveUp()) {
			nmoves++;
			matrix.MakeMove(MoveDirection.UP);
			repaint();
			return;
		}
	}

	public void commandAction(Command c, Displayable d) {
		// TODO Auto-generated method stub
		switch (((GameCommand) c).tag) {
		case CMD_EXIT:
			try {
				midlet.destroyApp(true);
			} catch (MIDletStateChangeException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			setState(INIT);
			break;

		case CMD_PAUSE:
			setState(PAUSED);
			midlet.notifyPaused();
			break;

		case CMD_RESUME:
			setState(PLAYING);
			break;

		case CMD_NEW:
			NewGame();
			setState(PLAYING);
			repaint();
			break;

		case CMD_OPTIONS:
			break;

		case CMD_HELP:
			break;
		}
	}

	public void run() {
		// TODO Auto-generated method stub
		
	}
}

class GameCommand extends Command {

	int tag;

	public GameCommand(String label, int type, int pri, int tag) {
		super(label, type, pri);
		this.tag = tag;
	}
}
